package s3

import (
	"context"
	"fmt"
	"io"
	"os"
	"time"

	"code.byted.org/bytertc/whiteboard_server/biz/pkg/object_storage/credentialsprovider"
	"code.byted.org/bytertc/whiteboard_server/biz/pkg/object_storage/s3_base"
	"code.byted.org/bytertc/whiteboard_server/biz/pkg/util"
	"code.byted.org/gopkg/env"
	"github.com/pkg/errors"
)

type Client struct {
	config   *Config
	s3Client *s3_base.Client
}

func New(ctx context.Context, opts ...Option) (*Client, error) {
	config := &Config{}
	for _, opt := range opts {
		opt(config)
	}
	err := config.Valid()
	if err != nil {
		return nil, err
	}

	client := &Client{
		config: config,
	}

	if config.StorageType == StorageTypeVolc {
		provider, err := credentialsprovider.NewAssumeRoleProvider(credentialsprovider.UserOfVolc,
			fmt.Sprintf(credentialsprovider.RtcRoleRtnFormat, config.AccountId))
		if err != nil {
			return nil, errors.Wrap(err, "new assume role provider failed")
		}
		disableSSL := false
		if env.IsBoe() {
			disableSSL = true
		}
		client.s3Client, err = s3_base.New(ctx, config.Region, config.EndPoint, s3_base.WithProvider(provider), s3_base.WithDisableSSL(disableSSL))
		if err != nil {
			return nil, errors.Wrap(err, "new s3 client failed")
		}
	} else {
		client.s3Client, err = s3_base.New(ctx, config.Region, config.EndPoint, s3_base.WithAKSK(config.AccessKey, config.SecretKey))
		if err != nil {
			return nil, errors.Wrap(err, "new s3 client failed")
		}
	}

	return client, nil
}

func (c *Client) UploadFile(ctx context.Context, filename, key string) error {
	f, err := os.Open(filename)
	if err != nil {
		return errors.Wrap(err, "UploadFile failed")
	}
	defer f.Close()

	return c.s3Client.Upload(ctx, c.config.Bucket, key, f)
}

func (c *Client) Upload(ctx context.Context, key string, reader io.Reader, size int64, contentType string) error {
	var (
		err error
	)
	if err = util.SeekToStart(reader); err != nil {
		return err
	}
	err = c.s3Client.Upload(ctx, c.config.Bucket, key, reader)
	if err == nil {
		return nil
	}
	_ = util.SeekToStart(reader)
	return err
}

func (c *Client) Download(ctx context.Context, key string, f *os.File) error {
	var (
		err error
	)
	if err = f.Truncate(0); err != nil {
		return err
	}
	if _, err = f.Seek(0, io.SeekStart); err != nil {
		return err
	}
	return c.s3Client.Download(ctx, c.config.Bucket, key, f)
}

// ListPrefix keyPrefix and maxRecursionCnt are useless
func (c *Client) ListPrefix(ctx context.Context, prefix, keyPrefix string, maxRecursionCnt int) ([]string, error) {
	return c.s3Client.ListPrefix(ctx, c.config.Bucket, prefix)
}

func (c *Client) URL(ctx context.Context, key string) string {
	return c.s3Client.URL(ctx, c.config.Bucket, key)
}

func (c *Client) PreSignURL(ctx context.Context, key string, duration time.Duration) (string, error) {
	return c.s3Client.PresignURL(ctx, c.config.Bucket, key, duration)
}

func (c *Client) ObjectExists(ctx context.Context, key string) (bool, error) {
	return c.s3Client.ObjectExists(ctx, c.config.Bucket, key)
}

func (c *Client) Bucket() string {
	return c.config.Bucket
}
